#include "IpcSocket.h"
#include "../../socket/internal/SockData.h"
#include "../../algorithm/Algorithm.h"
#include "../../tools/ShareApi.h"

DEF_BEG_LIBTOOLS

CIpcSocket::CIpcSocket()
{

}

CIpcSocket::CIpcSocket(CBasicService* pServer) :CSocket(pServer)
{
}

CIpcSocket::~CIpcSocket()
{
}

#ifdef STD11
CIpcSocket::CIpcSocket(CIpcSocket&& sock) :CSocket()
{
	_Close();
	Swap(sock);
}

CIpcSocket& CIpcSocket::operator= (CIpcSocket&& sock)
{
	if (this != &sock)
	{
		Close();
		Swap(sock);
	}
	return *this;
}
#endif // STD11

void CIpcSocket::Swap(CIpcSocket& sock)
{
	if (this != &sock)
	{
		dynamic_cast<CSocket*>(this)->Swap(*(dynamic_cast<CSocket*>(&sock)));
		this->Swap(sock);
	}
}

void CIpcSocket::SetAddr(const char* name)
{
#ifdef WIN32
	if (strncmp(name, "\\\\.\\pipe\\", 9) != 0) 
	{
		std::string pipName = "\\\\.\\pipe\\";
		pipName += name;
		CSocket::SetAddr(pipName.data());
	}
	else {
		CSocket::SetAddr(name);
	}
#elif defined(LINUX)
	if (strrchr(name, '/') == NullPtr) 
	{
		char curPath[MAX_PATH];
		ShareApi::GetCurrentDirectory(sizeof(curPath), curPath);
		std::string uxPath = curPath; uxPath += '/'; uxPath += name;
		CSocket::SetAddr(uxPath.c_str());
	}
	else {
		CSocket::SetAddr(name);
	}
#endif
}

CSockError CIpcSocket::WaitNamedPipe(DWORD timeout) const
{
	CSockError ec;
#ifdef WIN32
	if (::WaitNamedPipeA(GetAddr().c_str(), timeout) == FALSE)
		ec.SetError(CSockError::SYS_ERROR);
#endif
	return ec;
}

CSockError CIpcSocket::Socket(SOCKFAMILY::SockFamily domain /* = SOCKFAMILY::IPC */, SOCKTYPE::SockType type /* = SOCKTYPE::STREAM */,
	SOCKPROTOCOL::SockProtocol protocol /* = SOCKPROTOCOL::IPC_OPEN */)
{
	CSockError ec(CSockError::SUCC);
	SetProtocol(protocol);
	SetSockType(type);
	SetDomain(domain);
#ifdef WIN32
	if (protocol == SOCKPROTOCOL::IPC_CREATE)
	{
		DWORD dwMode = 0;
		if (type == SOCKTYPE::DGRAM) dwMode = PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE;
		else dwMode = PIPE_TYPE_BYTE | PIPE_TYPE_MESSAGE;
		dwMode |= PIPE_WAIT;
		HANDLE pip = CreateNamedPipeA(GetAddr().c_str(), PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED, dwMode, PIPE_UNLIMITED_INSTANCES, 65536, 65536, 0, NullPtr);
		if(pip == INVALID_HANDLE_VALUE) ec.SetError(CSockError::SYS_ERROR);
		SetSocket((SOCKET)pip);
	}
	else
	{
		HANDLE pip = CreateFileA(GetAddr().c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NullPtr, 
			OPEN_ALWAYS, FILE_FLAG_OVERLAPPED, NullPtr);
		if (pip == INVALID_HANDLE_VALUE) ec.SetError(CSockError::SYS_ERROR);
		SetSocket((SOCKET)pip);
	}
#elif defined(LINUX)
	int s = socket(AF_UNIX, type, 0);
	if (s == -1) ec.SetSysError(CSockError::SYS_ERROR);
	SetSocket(s);
#endif
	
	return ec;
}

CSockError CIpcSocket::SetNolock()
{
#ifdef WIN32
	return CSockError(CSockError::SUCC);
#elif defined(LINUX)
	return CSocket::SetNolock();
#endif
}

int CIpcSocket::_Close()
{
#ifdef WIN32
	BOOL b1, b2;
	if (*GetSockPtr() != INVALID_SOCKET)
	{
		b1 = ::DisconnectNamedPipe((HANDLE)*GetSockPtr());
		b2 = ::CloseHandle((HANDLE)*GetSockPtr());
		SetSocket(INVALID_SOCKET);
	}
	if (b1 == FALSE || b2 == FALSE) return -1;

	return 0;
#elif defined(LINUX)
	return CSocket::_Close();
#endif
}

DEF_END_LIBTOOLS